/**
 * Created by Richard Radlherr.
 * Date: 29.06.2006 01:51
 */

using System;
using System.Collections.Generic;
using System.Reflection;

namespace TagTool.Core
{
	/**
	 * This delegate is used for traversing over the node- type- tree exposed by the AssemblyManager.
	 * TODO: use this delegate to create the Strings for the treeview of the NodeBrowsePanel.
	 */
	public delegate void AssemblyTraverseCallback(String assembly, Type nodeType, bool newAssembly);

	/**
	 * The Assembly Manager is reesponible for adding assemblies into the 
	 * "available nodes - list", inspection of that assemblies and creation of nodes
	 * residing in the Assemby manged by a specific instance of the Assembly manager.
	 * An insance of the Manager can only exist for one loaded Assembly.
	 * So the AssemblyManager is also a resource pool for instances of AssemblyManagers
	 * which are refernceable via the assemblname.
	 * The Assembly Manager also Instanciates nodes from a given Assembly and typename.
	 */
	public class AssemblyManager
	{
		/**
		 * The assembly this manager manages.
		 */
		private Assembly assembly = null;
		
		/**
		 * The list of BaseNode- Types in the assembly
		 */
		private SortedList<String, Type> nodeTypeList = new SortedList<String, Type>();
		
		/**
		 * The Assembly resource pool - list
		 */
		private static SortedList<String, AssemblyManager> assemblyManagers = new SortedList<String, AssemblyManager>();
		
		/**
		 * Return the requested AssemblyManager.
		 * If a Manager with the given name is not in the resource pool
		 * the method trys to load an assembly of that name and adds it
		 * if it contains any Datatype derived from BaseNode.
		 */
		public static AssemblyManager GetInstance(String assemblyName)
		{
			AssemblyManager result = null;
			
			if (!assemblyManagers.ContainsKey(assemblyName))
			{
				
				Assembly assembly = null;

				try
				{
					assembly = Assembly.LoadFrom(assemblyName);
					result = new AssemblyManager(assembly);

					if (!result.InspectAssembly())
					{
						result = null;
					}
					else
					{
						assemblyManagers.Add(assemblyName, result);
					}
				}
				catch 
				{}
			}
			else
			{
				result = assemblyManagers[assemblyName];
			}
			return result;
		}
		
		/**
		 * The constructor is private
		 */
		private AssemblyManager(Assembly assembly)
		{
			this.assembly = assembly;
		}
		
		/**
		 * Returns the assembly mangad by this instance of AssemblyManager.
		 */
		public Assembly Assembly
		{
			get { return assembly; }
		}
		
		/**
		 * Returns true ist this specific manager contains the requesetd node type.
		 */
		public bool ContainsNodeType(String name)
		{
			return nodeTypeList.ContainsKey(name);
		}
		
		public static bool ContainsAssembly(String assembly)
		{
			return assemblyManagers.ContainsKey(assembly);
		}
		
		public BaseNode Create(String name)
		{
			BaseNode node = null;
			if (nodeTypeList.ContainsKey(name))
			{
				node = (BaseNode)assembly.CreateInstance(nodeTypeList[name].FullName);
			}
			return node;
		}
		
		public bool InspectAssembly()
		{
			Type[] typeArray = assembly.GetTypes ();
			foreach (Type type in typeArray) 
			{
				//if (type.BaseType.FullName == typeof(BaseNode).FullName || type.BaseType.FullName == typeof(RenderableNode).FullName)
				if (type.IsSubclassOf(typeof(BaseNode)) && !type.IsGenericTypeDefinition)
				{
					nodeTypeList.Add(type.FullName, type);
				}
			}
			return nodeTypeList.Count > 0;
		}
		
		public static void Remove(String assemblyname)
		{
			assemblyManagers.Remove(assemblyname);
		}

		public static void Remove(String assemblyname, String nodeTypeName)
		{
			assemblyManagers.Remove(assemblyname);
		}
		
		public static void TraverseAssemblies(AssemblyTraverseCallback callback)
		{

			foreach(KeyValuePair<String, AssemblyManager> e in assemblyManagers)
			{
				callback(e.Key, null, true);

				foreach(KeyValuePair<String, Type> f in e.Value.nodeTypeList)
				{
					callback(e.Key, f.Value, false);
				}
			}
		}
		
		public static void TraverseAssembliy(String assembly, AssemblyTraverseCallback callback)
		{
			if (assemblyManagers.ContainsKey(assembly))
			{
				AssemblyManager m = assemblyManagers[assembly];
				
				callback(assembly, null, true);

				foreach(KeyValuePair<String, Type> f in m.nodeTypeList)
				{
					callback(assembly, f.Value, false);
				}
			}
		}
		
	}

}
